package com.totem.customer.serivce.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.totem.admin.vo.FundBalanceVO;
import com.totem.admin.vo.VerificationCodeCheckVO;
import com.totem.base.CacheHolder;
import com.totem.base.constants.*;
import com.totem.base.enums.*;
import com.totem.base.exception.BaseException;
import com.totem.base.model.PageVO;
import com.totem.base.model.Result;
import com.totem.base.mybatis.UserIdContext;
import com.totem.base.util.DateUtils;
import com.totem.base.util.RandomValueUtils;
import com.totem.base.util.WalletGeneratorUtils;
import com.totem.customer.mapper.*;
import com.totem.customer.model.*;
import com.totem.customer.serivce.IMemberCommonService;
import com.totem.customer.serivce.ITCustomerService;
import com.totem.customer.vo.*;
import com.totem.order.mapper.TOrderMapper;
import com.totem.order.mapper.TSubscribeOrderItemMapper;
import com.totem.order.mapper.TWalletJournalMapper;
import com.totem.order.mapper.TWalletMapper;
import com.totem.order.model.TOrders;
import com.totem.order.model.TSubscribeOrderItems;
import com.totem.order.model.TWalletJournal;
import com.totem.order.model.TWallets;
import com.totem.order.service.IOrderCommonService;
import com.totem.order.service.ITSubscribeOrderItemService;
import com.totem.order.vo.CollectVO;
import com.totem.product.mapper.TSubscribeMapper;
import com.totem.product.model.TSubscribe;
import com.totem.product.service.ITSubscribeService;
import com.totem.startup.InitialGlobalConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bitcoinj.crypto.MnemonicException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.Executor;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
@Service
public class TCustomerServiceImpl extends ServiceImpl<TCustomerMapper, TCustomers> implements ITCustomerService {
    private final Argon2PasswordEncoder argon2Encoder = SecurityComponets.ARGON2_PASSWORD_ENCODER;
    @Autowired
    private TCustomerMapper tCustomerMapper;
    @Autowired
    private TLoginJournalMapper tLoginJournalMapper;
    @Autowired
    private TFavoritesMapper tFavoritesMapper;
    @Autowired
    private TTracksMapper tTracksMapper;
    @Autowired
    private TInviteMapper tInviteMapper;
    @Autowired
    private TMemberMapper tMemberMapper;
    @Autowired
    private IMemberCommonService iMemberCommonService;
    @Autowired
    private TOrderMapper tOrderMapper;
    @Autowired
    private TSubscribeOrderItemMapper tSubscribeOrderItemMapper;
    @Autowired
    private ITSubscribeOrderItemService itSubscribeOrderItemService;
    @Autowired
    private TSubscribeMapper tSubscribeMapper;
    @Autowired
    private TWalletMapper tWalletMapper;
    @Autowired
    private TWalletJournalMapper tWalletJournalMapper;
    @Autowired
    private IOrderCommonService iOrderCommonService;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private Executor asyncExecutor;
    @Autowired
    private CacheHolder cacheHolder;

    @Override
    public List<Long> initTestData() {
        int topUsers = 1;
        int perLevelMaxCount = 2;
        int randomMinDeep = 1;
        int randomMaxDeep = 2;

        List<TCustomers> custList = super.list();
        if(!CollectionUtils.isEmpty(custList) && custList.size()>3){
            return custList.stream().map(TCustomers::getId).toList();
        }
        for(int i=0; i<topUsers; i++) {
            TCustomers rc = buildRandomCustomer();
            CustomerCreateVO vo = new CustomerCreateVO();
            BeanUtils.copyProperties(rc, vo);
            vo.setLoginType(LoginTypeEnum.EMAIL.getCode());
            vo.setRepeatPassword(rc.getPassword());
            vo.setRepeatTxPassword(rc.getTxPassword());
            createCustomer(vo);
        }

        LambdaQueryWrapper<TCustomers> query = new LambdaQueryWrapper<>();
        query.ne(TCustomers::getId, custList);
        custList = tCustomerMapper.selectList(query);
        List<String> inviteCodes = custList.stream().map(TCustomers::getInviteCode).toList();
        for(int i=0; i<custList.size(); i++){
            int depth = RandomValueUtils.getInt(randomMinDeep, randomMaxDeep);
            int idx = RandomValueUtils.getInt(0, topUsers);
            createInviteeCustomer(depth, inviteCodes.get(idx), perLevelMaxCount);
        }
        // 查出所有测试客户,以便测试使用
        custList = tCustomerMapper.selectList(query);

        return custList.stream().map(TCustomers::getId).toList();
    }

    private void createInviteeCustomer(int depth, String inviteCode, int perLevelMaxCount){
        if(depth==0) return;

        int perLevelCount = RandomValueUtils.getInt(1, perLevelMaxCount);
        for(int i=0; i<perLevelCount; i++) {
            TCustomers rc = buildRandomCustomer();
            CustomerCreateVO vo = new CustomerCreateVO();
            BeanUtils.copyProperties(rc, vo);
            vo.setInviteCode(inviteCode);
            vo.setLoginType(LoginTypeEnum.EMAIL.getCode());
            vo.setRepeatPassword(rc.getPassword());
            vo.setRepeatTxPassword(rc.getTxPassword());
            createCustomer(vo);
            createInviteeCustomer(depth-1, vo.getInviteCode(), perLevelMaxCount);
        }
    }

    @Override
    public boolean createRandomCustomer(int count) {
        if(count<=0) return true;

        List<TCustomers> list = new ArrayList<>();
        for(int i=0; i<count; i++){
            TCustomers tCustomers = buildRandomCustomer();
            list.add(tCustomers);
        }
        saveBatch(list);
        return true;
    }

    @Override
    public TCustomers findByNickname(String nickname) {
        LambdaQueryWrapper<TCustomers> findWrapper = new LambdaQueryWrapper<>();
        findWrapper.eq(TCustomers::getNickname, nickname);
        return tCustomerMapper.selectOne(findWrapper);
    }

    @Override
    public TCustomers findByMobile(String countryCode, String mobile) {
        LambdaQueryWrapper<TCustomers> findWrapper = new LambdaQueryWrapper<>();
        findWrapper.eq(TCustomers::getCountryCode, countryCode);
        findWrapper.eq(TCustomers::getMobile, mobile);
        return tCustomerMapper.selectOne(findWrapper);
    }

    @Override
    public TCustomers findByEmail(String email) {
        LambdaQueryWrapper<TCustomers> findWrapper = new LambdaQueryWrapper<>();
        findWrapper.eq(TCustomers::getEmail, email);
        return tCustomerMapper.selectOne(findWrapper);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public boolean createCustomer(CustomerCreateVO vo) {
        String countryCode = vo.getCountryCode();
        String mobile = vo.getMobile();
        String email = vo.getEmail();
        String nickname= vo.getNickname();
        String pwd = vo.getPassword();
        String rPwd = vo.getRepeatPassword();
        String txPwd = vo.getTxPassword();
        String rTxPwd = vo.getRepeatTxPassword();
        if(StringUtils.isBlank(pwd) || StringUtils.isBlank(rPwd)){
            throw new BaseException(ErrorCode.REQUIRED_PASSWORD);
        }
        if(!pwd.equalsIgnoreCase(rPwd)){
            throw new BaseException(ErrorCode.PASSWORD_ERROR);
        }
        pwd = pwd.trim();
        if(pwd.length()<8 || pwd.length()>16){
            throw new BaseException(ErrorCode.PASSWORD_ERROR);
        }
        String pwdRegex = "^[a-zA-Z0-9]{8,16}$";
        if(!Pattern.matches(pwdRegex, pwd)){
            log.warn("密码格式错误, [{}]", pwd);
            throw new BaseException(ErrorCode.PASSWORD_ERROR);
        }
        if(StringUtils.isBlank(txPwd) || StringUtils.isBlank(rTxPwd)){
            throw new BaseException(ErrorCode.REQUIRED_TX_PASSWORD);
        }
        if(!txPwd.equalsIgnoreCase(rTxPwd)){
            throw new BaseException(ErrorCode.TX_PASSWORD_ERROR);
        }
        txPwd = txPwd.trim();
        if(txPwd.length()!=6){
            throw new BaseException(ErrorCode.TX_PASSWORD_ERROR);
        }
        String txPwdRegex = "^[0-9]{6}$";
        if(!Pattern.matches(txPwdRegex, txPwd)){
            log.warn("交易密码格式错误, [{}]", txPwd);
            throw new BaseException(ErrorCode.TX_PASSWORD_ERROR);
        }
        VerificationCodeCheckVO checkVO = new VerificationCodeCheckVO();
        BeanUtils.copyProperties(vo, checkVO);
        checkVO.setInputCode(vo.getVerificationCode());
        if(!verificationCodeCheck(checkVO)){
            throw new BaseException(ErrorCode.ERROR_VERIFICATION_CODE);
        }
        LambdaQueryWrapper<TCustomers> custQuery = new LambdaQueryWrapper<>();
        if(LoginTypeEnum.isMobile(vo.getLoginType())){
            if(StringUtils.isBlank(countryCode) || StringUtils.isBlank(mobile)){
                throw new BaseException(ErrorCode.REQUIRED_MOBILE);
            }
            custQuery.eq(TCustomers::getCountryCode, countryCode);
            custQuery.eq(TCustomers::getMobile, mobile);
            TCustomers one = tCustomerMapper.selectOne(custQuery);
            if(one!=null){
                throw new BaseException(ErrorCode.EXISTS_MOBILE);
            }
            // 手机号注册时,手机号作为昵称
            vo.setNickname(vo.getCountryCode()+"-"+vo.getMobile());
            vo.setEmail("");
        }else if(LoginTypeEnum.isEmail(vo.getLoginType())){
            if(StringUtils.isBlank(email) || StringUtils.isBlank(nickname)){
                throw new BaseException(ErrorCode.REQUIRED_EMAIL);
            }
            custQuery.eq(TCustomers::getNickname, nickname);
            custQuery.or().eq(TCustomers::getEmail, email);
            List<TCustomers> oneList = tCustomerMapper.selectList(custQuery);
            if(!CollectionUtils.isEmpty(oneList)){
                throw new BaseException(ErrorCode.EXISTS_EMAIL_NICKNAME);
            }
            vo.setCountryCode("");
            vo.setMobile("");
        }else{
            throw new BaseException(ErrorCode.ILLEGAL_LOGIN_TYPE);
        }
        TCustomers tCustomers = TCustomers.builder().build();
        BeanUtils.copyProperties(vo, tCustomers);
        tCustomers.setPassword(argon2Encoder.encode(pwd));
        tCustomers.setState(StateEnum.NORMAL.getCode());
        tCustomers.setFundAutoInvest(SwitchEnum.OFF.getCode());
        if(StringUtils.isBlank(tCustomers.getAvatarUrl())){
            tCustomers.setAvatarUrl(CommonConstants.DEFAULT_AVATAR_URL);
        }

        String inviterCode = vo.getInviteCode();
        Long inviterId = 0L;
        LambdaQueryWrapper<TCustomers> queryWrapper = null;
        if(StringUtils.isNotBlank(inviterCode)){
            queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TCustomers::getInviteCode, inviterCode);
            TCustomers customerDB = tCustomerMapper.selectOne(queryWrapper);
            if(customerDB!=null) {
                if (StateEnum.STOP.getCode().equals(customerDB.getState())) {
                    log.warn("用户[{}]已被封号,无法邀请", customerDB.getId());
                    throw new BaseException(ErrorCode.CANNOT_USE_INVITE_CODE);
                }
                inviterId = customerDB.getId();
            }
        }
        String selfInviteCode = RandomValueUtils.capitalNumber(6);
        LambdaQueryWrapper<TCustomers> findWrapper = new LambdaQueryWrapper<>();
        findWrapper.eq(TCustomers::getInviteCode, selfInviteCode);
        boolean exists = tCustomerMapper.exists(findWrapper);
        while(exists){
            findWrapper.clear();
            selfInviteCode = RandomValueUtils.capitalNumber(6);
            findWrapper.eq(TCustomers::getInviteCode, selfInviteCode);
            exists = tCustomerMapper.exists(findWrapper);
        }

        tCustomers.setInviteCode(selfInviteCode);
        int effect = tCustomerMapper.insert(tCustomers);

        String walletAddr = null;
        // 生成钱包并保存地址
        ResponseEntity<Result> gwResp = restTemplate.postForEntity(UrlConstants.API_GENERATE_WALLET_URL, null, Result.class);
        if (gwResp.getStatusCode() == HttpStatus.OK) {
            Result result = gwResp.getBody();
            if(result!=null){
                Map data = (Map)result.getData();
                walletAddr = (String)data.get("address");
            }else{
                log.warn("生成钱包失败");
                throw new BaseException(ErrorCode.GENERATE_FAILED);
            }
        } else {
            log.warn("生成钱包失败");
            throw new BaseException(ErrorCode.GENERATE_FAILED);
        }

        TWallets tWallets = TWallets.builder()
                .customerId(tCustomers.getId())
                .addr(walletAddr)
                .build();
        tWalletMapper.insert(tWallets);

        TMembers tMembers = TMembers.builder()
                .customerId(tCustomers.getId())
                .build();
        tMemberMapper.insert(tMembers);

        if(inviterId>0){
            iMemberCommonService.execInvite(inviterId, tCustomers.getId(), true, "");
            vo.setInviteCode(selfInviteCode);
        }
        return effect>0;
    }

    private String defaultPassword() {
        return CommonConstants.DEFAULT_PWD;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public boolean updateCustomer(CustomerUpdateVO vo) {
        if(vo.getCustomerId()==null){
            throw new BaseException(ErrorCode.REQUIRED_CUSTOMER_ID);
        }
        Long customerId = vo.getCustomerId();
        // 前端不判断是否更新,新旧数据都可能有
        String nickname = vo.getNickname();
        String countryCode = vo.getCountryCode();
        String mobile = vo.getMobile();
        String email = vo.getEmail();
        String state = vo.getState();
        boolean changeState = false;
        Integer newMemberLevel = vo.getMemberLevel();
        TCustomers one = tCustomerMapper.selectById(customerId);
        if(one==null){
            throw new BaseException(ErrorCode.NOT_EXIST_CUSTOMER);
        }
        if(StringUtils.isNotBlank(state)){
            StateEnum stateEnum = StateEnum.byCode(state);
            if(stateEnum==null){
                throw new BaseException(ErrorCode.ILLEGAL_STATE);
            }
            if(!state.equalsIgnoreCase(one.getState())){
                changeState = true;
            }
        }
        LambdaQueryWrapper<TCustomers> query = new LambdaQueryWrapper<>();
        if(StringUtils.isNotBlank(nickname)) {
            // 判断昵称是否需要修改
            if(!nickname.equalsIgnoreCase(one.getNickname())) {
                // 需要修改的昵称是否已被使用
                query.clear();
                query.eq(TCustomers::getNickname, nickname);
                TCustomers exists = tCustomerMapper.selectOne(query);
                if (exists != null) {
                    throw new BaseException(ErrorCode.EXISTS_NICKNAME);
                }
            }
        }
        if(StringUtils.isNotBlank(email)) {
            // 判断邮箱是否需要修改
            if(!email.equalsIgnoreCase(one.getEmail())) {
                // 需要修改的邮箱是否已被使用
                query.clear();
                query.eq(TCustomers::getEmail, email);
                TCustomers exists = tCustomerMapper.selectOne(query);
                if (exists != null) {
                    throw new BaseException(ErrorCode.EXISTS_EMAIL);
                }
            }
        }
        if(StringUtils.isNotBlank(countryCode) || StringUtils.isNotBlank(mobile)) {
            // 判断邮箱是否需要修改
            if(!mobile.equalsIgnoreCase(one.getMobile()) || !countryCode.equalsIgnoreCase(one.getCountryCode())) {
                // 需要修改的邮箱是否已被使用
                query.clear();
                query.eq(TCustomers::getCountryCode, countryCode);
                query.eq(TCustomers::getMobile, mobile);
                TCustomers exists = tCustomerMapper.selectOne(query);
                if (exists != null) {
                    throw new BaseException(ErrorCode.EXISTS_MOBILE);
                }
            }
        }
        Date now = new Date();
        TCustomers tCustomer = TCustomers.builder().build();
        BeanUtils.copyProperties(vo, tCustomer);
        tCustomer.setId(customerId);
        tCustomerMapper.updateById(tCustomer);

        TMembers member = tMemberMapper.selectById(customerId);
        if(member==null){
            throw new BaseException(ErrorCode.NOT_EXIST_MEMBER);
        }
        Integer custMemberLevel = member.getMemberLevel();
        Integer custEnjoyLevel = member.getEnjoyLevel();
        TMembers updateMember = TMembers.builder()
                .customerId(customerId)
                .build();
        if(newMemberLevel!=null) {
            if (!Objects.equals(custMemberLevel, newMemberLevel)) {
                updateMember.setMemberLevel(newMemberLevel);
                updateMember.setEnjoyLevel(newMemberLevel);
            }
            tMemberMapper.updateById(updateMember);
        }

        // 查询团队成员
        LambdaQueryWrapper<TInvite> teamQuery = new LambdaQueryWrapper<>();
        teamQuery.like(TInvite::getPath, customerId);
        List<TInvite> teamInviteList = tInviteMapper.selectList(teamQuery);
        List<Long> teamMemberIdList = teamInviteList.stream().map(TInvite::getInviteeId).toList();

        if(changeState) {
            LambdaQueryWrapper<TSubscribe> subscribeQuery = new LambdaQueryWrapper<>();
            List<TSubscribe> subscribeList = tSubscribeMapper.selectList(subscribeQuery);
            if(CollectionUtils.isEmpty(subscribeList)){
                log.warn("未找到认购包配置");
                return false;
            }
            Map<String, TSubscribe> subscribeMap = subscribeList.stream().collect(Collectors.toMap(TSubscribe::getSubscribeName, Function.identity()));

            itSubscribeOrderItemService.updateReturnHandleState(List.of(customerId), state, subscribeMap);

            // 团队封号
            if (!CollectionUtils.isEmpty(teamMemberIdList)) {
                List<TCustomers> teamCustList = tCustomerMapper.selectBatchIds(teamMemberIdList);
                List<TCustomers> stopList = teamCustList.stream().filter(c -> {
                    if(StateEnum.isNormal(state)) {
                        return StateEnum.isStop(c.getState());
                    }else{
                        return StateEnum.isNormal(c.getState());
                    }
                }).map(c -> {
                    TCustomers update = TCustomers.builder().build();
                    update.setId(c.getId());
                    update.setState(state);
                    return update;
                }).toList();

                super.updateBatchById(stopList);

                itSubscribeOrderItemService.updateReturnHandleState(teamMemberIdList, state, subscribeMap);
            }
        }
        return true;
    }

    @Override
    public PageVO<CustomerVO> customerPage(CustomerPageVO vo) {
        Page<TCustomers> page = new Page<>();
        if(vo.getCurrent()>0 && vo.getPageSize()>0) {
            page.setCurrent(vo.getCurrent());
            page.setSize(vo.getPageSize());
        }

        //page.setOrders(vo.getOrders());
        LambdaQueryWrapper<TCustomers> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(vo.getNickname()), TCustomers::getNickname, vo.getNickname())
                .eq(StringUtils.isNotBlank(vo.getCountryCode()), TCustomers::getCountryCode, vo.getCountryCode())
                .like(StringUtils.isNotBlank(vo.getMobile()), TCustomers::getMobile, vo.getMobile())
                .like(StringUtils.isNotBlank(vo.getEmail()), TCustomers::getEmail, vo.getEmail())
                //.eq(TCustomers::getState, StateEnum.NORMAL.getCode())
                .orderByDesc(TCustomers::getCreateTime)
        ;
        List<TCustomers> customerList = super.page(page, queryWrapper).getRecords();
        if(CollectionUtils.isEmpty(customerList)){
            PageVO<CustomerVO> pageVO = new PageVO<>();
            pageVO.setList(List.of());
            pageVO.setTotal(0L);
            return pageVO;
        }
        List<Long> customerIdList = customerList.stream().map(TCustomers::getId).toList();

        List<TLoginJournal> loginJournalList = tLoginJournalMapper.findMaxMin(customerIdList);
        Map<Long, List<TLoginJournal>> loginJournalMap = loginJournalList.stream().collect(Collectors.groupingBy(TLoginJournal::getCustomerId));

        List<TWallets> walletsList = tWalletMapper.selectBatchIds(customerIdList);
        Map<Long, TWallets> walletsMap = walletsList.stream().collect(Collectors.toMap(TWallets::getCustomerId, Function.identity()));

        List<TMembers> memberList = tMemberMapper.selectBatchIds(customerIdList);
        Map<Long, TMembers> memberMap = memberList.stream().collect(Collectors.toMap(TMembers::getCustomerId, Function.identity()));

        LambdaQueryWrapper<TInvite> inviteQuery = new LambdaQueryWrapper<>();
        inviteQuery.in(TInvite::getInviteeId, customerIdList);
        List<TInvite> inviteList = tInviteMapper.selectList(inviteQuery);
        Map<Long, Long> inviteMap = new HashMap<>();
        Map<Long, List<Long>> parentPathMap = new HashMap<>();
        Map<Long, String> allParentNicknameMap = new HashMap<>();
        if(!CollectionUtils.isEmpty(inviteList)) {
            Map<Long, Long> inviteTmpMap = inviteList.stream().collect(Collectors.toMap(TInvite::getInviteeId, TInvite::getInviterId));
            inviteMap.putAll(inviteTmpMap);

            Map<Long, List<Long>> parentPathTempMap = inviteList.stream()
                    .collect(Collectors.toMap(
                            TInvite::getInviteeId,
                            v -> Arrays.stream(v.getPath().split(",")).map(Long::valueOf).toList()));
            parentPathMap.putAll(parentPathTempMap);
            List<Long> allParentIdList = new ArrayList<>();
            parentPathMap.entrySet().forEach(c -> allParentIdList.addAll(c.getValue()));
            List<TCustomers> allParentList = tCustomerMapper.selectBatchIds(allParentIdList);
            Map<Long, String> allParentNicknameTempMap = allParentList.stream().collect(Collectors.toMap(TCustomers::getId, TCustomers::getNickname));
            allParentNicknameMap.putAll(allParentNicknameTempMap);
        }

        List<Long> inviterIdList = inviteList.stream().map(TInvite::getInviterId).toList();
        Map<Long, String> inviterNickanmeMap = new HashMap<>();
        if(!CollectionUtils.isEmpty(inviterIdList)) {
            List<TCustomers> inviterList = tCustomerMapper.selectBatchIds(inviterIdList);
            Map<Long, String> inviterNickname = inviterList.stream().collect(Collectors.toMap(TCustomers::getId, TCustomers::getNickname));
            inviterNickanmeMap.putAll(inviterNickname);
        }

        List<TWallets> walletList = tWalletMapper.selectBatchIds(customerIdList);
        Map<Long, TWallets> walletMap = walletList.stream().collect(Collectors.toMap(TWallets::getCustomerId, Function.identity()));

        List<CustomerVO> voList = customerList.stream().map(entity->{
            Long customerId = entity.getId();
            TWallets tWallet = walletsMap.get(customerId);
            Long inviterId = inviteMap.get(customerId);
            String inviterNickname = inviterNickanmeMap.get(inviterId);
            List<Long> custInviterIdList = parentPathMap.get(customerId);
            TWallets wallet = walletMap.get(customerId);
            TMembers member = memberMap.get(customerId);
            List<TLoginJournal> loginJournal = loginJournalMap.get(customerId);

            CustomerVO info = new CustomerVO();
            BeanUtils.copyProperties(entity, info);
            info.setCustomerId(entity.getId());
            info.setInviterId(inviterId);
            info.setInviterName(inviterNickname);
            if(!CollectionUtils.isEmpty(custInviterIdList)){
                List<InviteVO> inviteVOList = new ArrayList<>();
                custInviterIdList.forEach(custInviterId->{
                    String nickname = allParentNicknameMap.get(custInviterId);
                    InviteVO inviteVO = new InviteVO();
                    inviteVO.setInviteeId(customerId);
                    inviteVO.setInviterId(custInviterId);
                    inviteVO.setInviterNickname(nickname);
                    inviteVOList.add(inviteVO);
                });
                info.setInviterList(inviteVOList);
            }
            if(wallet!=null) {
                info.setUsdo(wallet.getUsdo());
                info.setUsdt(wallet.getUsdt());
            }
            if(member!=null){
               info.setMemberLevel(member.getEnjoyLevel());
            }
            if(!CollectionUtils.isEmpty(loginJournal)){
                TLoginJournal firstJournal = loginJournal.get(0);
                String firstTime = DateUtils.formatDate(firstJournal.getCreateTime(), CommonConstants.DEFAULT_DATETIME_FORMAT);
                info.setFirstAccessTime(firstTime);
                if(loginJournal.size()>1) {
                    TLoginJournal latestJournal = loginJournal.get(1);
                    String latestTime = DateUtils.formatDate(latestJournal.getCreateTime(), CommonConstants.DEFAULT_DATETIME_FORMAT);
                    info.setLatestAccessTime(latestTime);
                }
            }
            if(tWallet!=null){
                info.setFund(tWallet.getFund());
            }
            return info;
        }).collect(Collectors.toList());

        PageVO<CustomerVO> pageVO = new PageVO<>();
        BeanUtils.copyProperties(vo, pageVO);
        pageVO.setList(voList);
        pageVO.setTotal(page.getTotal());
        return pageVO;
    }

    @Override
    public CustomerProfileVO profile() {
        Long customerId = UserIdContext.getUserId();
        if(customerId==null){
            throw new BaseException(ErrorCode.UNAUTHORIZED);
        }

        TCustomers tCustomers = getById(customerId);

        LambdaQueryWrapper<TWallets> walletQuery = new LambdaQueryWrapper<>();
        walletQuery.eq(TWallets::getCustomerId, customerId);
        TWallets myWallet = tWalletMapper.selectOne(walletQuery);
        if(myWallet==null){
            throw new BaseException(ErrorCode.NOT_EXIST_WALLET);
        }

        LambdaQueryWrapper<TFavorites> favoriteQuery = new LambdaQueryWrapper<>();
        favoriteQuery.eq(TFavorites::getCustomerId, customerId);
        Long favoriteCount = tFavoritesMapper.selectCount(favoriteQuery);

        LambdaQueryWrapper<TTracks> trackQuery = new LambdaQueryWrapper<>();
        trackQuery.eq(TTracks::getCustomerId, customerId);
        Long trackCount = tTracksMapper.selectCount(trackQuery);

        LambdaQueryWrapper<TWalletJournal> goodsJournalQuery = new LambdaQueryWrapper<>();
        goodsJournalQuery.eq(TWalletJournal::getCustomerId, customerId);
        goodsJournalQuery.eq(TWalletJournal::getJournalType, JournalTypeEnum.GOODS_ORDER.getCode());
        goodsJournalQuery.eq(TWalletJournal::getJournalState, JournalStateEnum.FINISHED.getCode());
        Long goodsOrderNum = tWalletJournalMapper.selectCount(goodsJournalQuery);

        TMembers member = tMemberMapper.selectById(customerId);

        CustomerProfileVO vo = new CustomerProfileVO();
        BeanUtils.copyProperties(tCustomers, vo);

        vo.setAddress(myWallet.getAddr());
        vo.setFundInvest(myWallet.getFund());
        vo.setFundEarnings(myWallet.getFundAccReturn());

        if(member!=null){
            vo.setMemberLevel(member.getMemberLevel());
            vo.setEnjoyLevel(member.getEnjoyLevel());

            if(member.getMemberLevel()<InitialGlobalConfig.TEAM_MOTIVATION_RULE.size()){
                int nextLevel = member.getMemberLevel()+1;
                TeamIncentiveVO next = InitialGlobalConfig.TEAM_MOTIVATION_RULE.get(nextLevel);
                Integer nextTeamCount = next.getTeamCount();
                Integer count = getUpgradeLevelCount(nextLevel, member);
                vo.setNextMemberLevelCount(nextTeamCount-count);

                BigDecimal personalAppraisal = next.getPersonalAppraisal();
                BigDecimal pa = member.getInvest();
                BigDecimal diffPa = personalAppraisal.subtract(pa);
                if(diffPa.compareTo(BigDecimal.ZERO)<=0) {
                    vo.setNextMemberLevelPersonalInvest(BigDecimal.ZERO);
                }else{
                    vo.setNextMemberLevelPersonalInvest(diffPa);
                }

                BigDecimal teamAppraisal = next.getTeamAppraisal();
                BigDecimal ta = member.getTeamInvest();
                BigDecimal diffTa = teamAppraisal.subtract(ta);
                if(diffTa.compareTo(BigDecimal.ZERO)<=0) {
                    vo.setNextMemberLevelTeamInvest(BigDecimal.ZERO);
                }else{
                    vo.setNextMemberLevelTeamInvest(diffTa);
                }
            }else{
                vo.setNextMemberLevelCount(0);
                vo.setNextMemberLevelPersonalInvest(BigDecimal.ZERO);
                vo.setNextMemberLevelTeamInvest(BigDecimal.ZERO);
            }
        }

        vo.setOrderNum(goodsOrderNum);
        vo.setUsdo(myWallet.getUsdo());
        vo.setUsdt(myWallet.getUsdt());
        vo.setFavoriteCount(favoriteCount);
        vo.setTrackCount(trackCount);
        return vo;
    }

    @Override
    public CustomerVO getVoById(Long id) {
        TCustomers tCustomers = getById(id);
        CustomerVO vo = new CustomerVO();
        BeanUtils.copyProperties(tCustomers, vo);
        return vo;
    }

    @Override
    public boolean updatePassword(UpdatePasswordVO vo) {
        String nickname = vo.getNickname();
        if(StringUtils.isBlank(nickname)){
            throw new BaseException(ErrorCode.REQUIRED_NICKNAME);
        }
        String pwd = vo.getPassword();
        if(StringUtils.isBlank(pwd)){
            throw new BaseException(ErrorCode.REQUIRED_PASSWORD);
        }
        if(pwd.length()<8 || pwd.length()>16){
            throw new BaseException(ErrorCode.PASSWORD_ERROR);
        }
        if(StringUtils.isBlank(vo.getRepeatPassword())){
            throw new BaseException(ErrorCode.PASSWORD_ERROR);
        }
        if(!vo.getPassword().equals(vo.getRepeatPassword())){
            throw new BaseException(ErrorCode.PASSWORD_ERROR);
        }
        LambdaQueryWrapper<TCustomers> custQuery = new LambdaQueryWrapper<>();
        custQuery.eq(TCustomers::getNickname, nickname);
        List<TCustomers> customerList = tCustomerMapper.selectList(custQuery);
        if(CollectionUtils.isEmpty(customerList)){
            log.warn("昵称[{}]用户不存在", nickname);
            throw new BaseException(ErrorCode.NOT_EXIST_CUSTOMER);
        }
        if(customerList.size()>1){
            log.warn("昵称[{}]重复了", nickname);
            throw new BaseException(ErrorCode.REPEAT_NICKNAME);
        }
        TCustomers tCustomers = customerList.get(0);
        Long customerId = tCustomers.getId();

        String oldPassword = tCustomers.getPassword();
        if(argon2Encoder.matches(vo.getPassword(), oldPassword)){
            throw new BaseException(ErrorCode.PASSWORD_OLD_ERROR);
        }
        String newPassword = argon2Encoder.encode(vo.getPassword());
        TCustomers updateCust = TCustomers.builder()
                .id(customerId)
                .password(newPassword)
                .build();
        int effect = tCustomerMapper.updateById(updateCust);

        if(effect>0){
            /*
            Object token = cacheHolder.get(CacheConstants.CACHE_TOKEN+customerId);
            if(Objects.nonNull(token)) {
                cacheHolder.remove(CacheConstants.CACHE_CERT + token);
            }
            cacheHolder.remove(CacheConstants.CACHE_TOKEN+customerId);
             */
            logout();
        }
        return effect>0;
    }

    @Override
    public boolean resetPassword(ResetPasswordVO vo) {
        Long customerId = vo.getCustomerId();
        if(customerId==null){
            throw new BaseException(ErrorCode.REQUIRED_CUSTOMER_ID);
        }
        String pwd = vo.getPassword();
        if(StringUtils.isBlank(pwd)){
            throw new BaseException(ErrorCode.REQUIRED_PASSWORD);
        }
        if(pwd.length()<8 || pwd.length()>16){
            throw new BaseException(ErrorCode.PASSWORD_ERROR);
        }
        if(StringUtils.isBlank(vo.getRepeatPassword())){
            throw new BaseException(ErrorCode.PASSWORD_ERROR);
        }
        if(!vo.getPassword().equals(vo.getRepeatPassword())){
            throw new BaseException(ErrorCode.PASSWORD_ERROR);
        }
        VerificationCodeCheckVO checkVO = new VerificationCodeCheckVO();
        BeanUtils.copyProperties(vo, checkVO);
        checkVO.setInputCode(vo.getVerificationCode());
        if(!verificationCodeCheck(checkVO)){
            throw new BaseException(ErrorCode.ERROR_VERIFICATION_CODE);
        }
        TCustomers tCustomers = getById(customerId);
        if(tCustomers==null){
            log.warn("Id为[{}]的客户不存在", customerId);
            throw new BaseException(ErrorCode.NOT_EXIST_CUSTOMER);
        }
        String oldPassword = tCustomers.getPassword();
        if(argon2Encoder.matches(vo.getPassword(), oldPassword)){
            throw new BaseException(ErrorCode.PASSWORD_OLD_ERROR);
        }
        String loginType = vo.getLoginType();
        if(LoginTypeEnum.isMobile(loginType)){
            String countryCode = vo.getCountryCode();
            String mobile = vo.getMobile();
            LambdaQueryWrapper<TCustomers> customerQuery = new LambdaQueryWrapper<>();
            customerQuery.eq(TCustomers::getCountryCode, countryCode);
            customerQuery.eq(TCustomers::getMobile, mobile);
            TCustomers one = tCustomerMapper.selectOne(customerQuery);
            if(one == null) {
                throw new BaseException(ErrorCode.NOT_EXIST_MOBILE);
            }
        }
        if(LoginTypeEnum.isEmail(loginType)){
            String email = vo.getEmail();
            LambdaQueryWrapper<TCustomers> customerQuery = new LambdaQueryWrapper<>();
            customerQuery.eq(TCustomers::getEmail, email);
            TCustomers one = tCustomerMapper.selectOne(customerQuery);
            if(one == null) {
                throw new BaseException(ErrorCode.NOT_EXIST_EMAIL);
            }
        }

        String newPassword = argon2Encoder.encode(vo.getPassword());
        TCustomers updateCust = TCustomers.builder()
                .id(customerId)
                .password(newPassword)
                .build();
        int effect = tCustomerMapper.updateById(updateCust);

        if(effect>0){
            logout();
        }
        return effect>0;
    }

    @Override
    public boolean updateTxPassword(UpdateTxPasswordVO vo) {
        Long customerId = UserIdContext.getUserId();
        if(customerId==null){
            throw new BaseException(ErrorCode.UNAUTHORIZED);
        }
        String pwd = vo.getTxPassword();
        if(StringUtils.isBlank(pwd)){
            throw new BaseException(ErrorCode.REQUIRED_TX_PASSWORD);
        }
        if(pwd.length()!=6) {
            throw new BaseException(ErrorCode.TX_PASSWORD_ERROR);
        }
        if(!StringUtils.isNumeric(pwd)){
            throw new BaseException(ErrorCode.TX_PASSWORD_ERROR);
        }
        String txPassword = argon2Encoder.encode(vo.getTxPassword());

        TCustomers updateCust = TCustomers.builder()
                .id(customerId)
                .txPassword(txPassword)
                .build();
        tCustomerMapper.updateById(updateCust);
        return true;
    }

    @Override
    public InviteVO searchByEmailOrMobile(SearchByEmailOrMobileVO vo) {
        TCustomers customers = null;
        if(StringUtils.isNotBlank(vo.getEmail())) {
            LambdaQueryWrapper<TCustomers> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TCustomers::getEmail, vo.getEmail());
            customers = tCustomerMapper.selectOne(queryWrapper);
        }else if(StringUtils.isNotBlank(vo.getCountryCode()) && StringUtils.isNotBlank(vo.getMobile())){
            LambdaQueryWrapper<TCustomers> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TCustomers::getCountryCode, vo.getCountryCode());
            queryWrapper.eq(TCustomers::getMobile, vo.getMobile());
            customers = tCustomerMapper.selectOne(queryWrapper);
        }else{
            throw new BaseException(ErrorCode.REQUIRED_EMAIL_MOBILE);
        }

        InviteVO inviteVO = new InviteVO();
        if(customers==null){
            return inviteVO;
        }
        inviteVO.setInviterNickname(customers.getNickname());
        inviteVO.setInviterId(customers.getId());
        return inviteVO;
    }

    @Override
    public boolean registerCheck(RegisterCheckVO vo) {
        TCustomers customers = null;
        if(StringUtils.isNotBlank(vo.getInviteCode())){
            LambdaQueryWrapper<TCustomers> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TCustomers::getInviteCode, vo.getInviteCode());
            customers = tCustomerMapper.selectOne(queryWrapper);
            if(customers==null){
                throw new BaseException(ErrorCode.CANNOT_USE_INVITE_CODE);
            }
        }
        if(StringUtils.isNotBlank(vo.getNickname())){
            LambdaQueryWrapper<TCustomers> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TCustomers::getNickname, vo.getNickname());
            customers = tCustomerMapper.selectOne(queryWrapper);
            if(customers!=null){
                throw new BaseException(ErrorCode.EXISTS_NICKNAME);
            }
        }
        if(StringUtils.isNotBlank(vo.getEmail())) {
            LambdaQueryWrapper<TCustomers> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TCustomers::getEmail, vo.getEmail());
            customers = tCustomerMapper.selectOne(queryWrapper);
            if(customers!=null){
                throw new BaseException(ErrorCode.EXISTS_EMAIL);
            }
        }
        if(StringUtils.isNotBlank(vo.getCountryCode()) && StringUtils.isNotBlank(vo.getMobile())){
            LambdaQueryWrapper<TCustomers> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(TCustomers::getCountryCode, vo.getCountryCode());
            queryWrapper.eq(TCustomers::getMobile, vo.getMobile());
            customers = tCustomerMapper.selectOne(queryWrapper);
            if(customers!=null){
                throw new BaseException(ErrorCode.EXISTS_MOBILE);
            }
        }
        return true;
    }

    @Override
    public TeamInfoVO teamInfo(Long customerId) {
        // Map<Long, FundBalanceVO> fundBalanceMap = iOrderCommonService.queryFundBalance(List.of(customerId));
        // FundBalanceVO fundBalanceVO = fundBalanceMap.get(customerId);
        LambdaQueryWrapper<TInvite> teamQuery = new LambdaQueryWrapper<>();
        teamQuery.like(TInvite::getPath, customerId);
        List<TInvite> teamList = tInviteMapper.selectList(teamQuery);
        List<Long> teamMemberIdList= new ArrayList<>(teamList.stream().map(TInvite::getInviteeId).collect(Collectors.toSet()));

        Map<Long, String> inviteeTimeMap = teamList.stream().collect(Collectors.toMap(
                TInvite::getInviteeId,
                v-> DateUtils.formatDate(v.getCreateTime(), CommonConstants.DEFAULT_DATETIME_FORMAT)));
        teamMemberIdList.add(customerId);

        List<TMembers> memberList = tMemberMapper.selectBatchIds(teamMemberIdList);
        Map<Long, TMembers> memberMap = memberList.stream().collect(Collectors.toMap(TMembers::getCustomerId, Function.identity()));
        TMembers top = memberMap.get(customerId);
        memberMap.remove(customerId);

        TeamInfoVO vo = new TeamInfoVO();
        vo.setCustomerId(customerId);
        vo.setAward(top.getAward());
        vo.setTeamAward(top.getTeamAward());
        vo.setInvest(top.getInvest());
        vo.setTeamInvest(top.getTeamInvest());
        vo.setDirectInvest(BigDecimal.ZERO);
        vo.setInviteCount(0L);
        vo.setTeamCount(0L);
        if(CollectionUtils.isEmpty(teamList)){
            return vo;
        }
        teamMemberIdList.remove(customerId);
        List<TCustomers> customerList = tCustomerMapper.selectBatchIds(teamMemberIdList);
        Map<Long, TCustomers> customerMap = customerList.stream().collect(Collectors.toMap(TCustomers::getId, Function.identity()));

        List<MemberVO> memberVoList = memberMap.entrySet().stream().map(entry->{
            TMembers v = entry.getValue();
            Long inviteeId = v.getCustomerId();
            String inviteTime = inviteeTimeMap.get(inviteeId);
            TCustomers invitee = customerMap.get(inviteeId);

            MemberVO mVo = new MemberVO();
            mVo.setCustomerId(inviteeId);
            mVo.setMemberLevel(v.getMemberLevel());
            mVo.setTeamInvest(v.getTeamInvest());
            mVo.setInviteeTime(inviteTime);
            if(invitee!=null){
                mVo.setNickname(invitee.getNickname());
                mVo.setCountryCode(invitee.getCountryCode());
                mVo.setMobile(invitee.getMobile());
                mVo.setEmail(invitee.getEmail());
            }
            return mVo;
        }).toList();

        vo.setTeamCount((long) customerList.size());

        LambdaQueryWrapper<TInvite> directQuery = new LambdaQueryWrapper<>();
        directQuery.eq(TInvite::getInviterId, customerId);
        List<TInvite> directList = tInviteMapper.selectList(directQuery);
        if(!CollectionUtils.isEmpty(directList)){
            List<Long> directIdList= new ArrayList<>(directList.stream().map(TInvite::getInviteeId).collect(Collectors.toSet()));
            List<TMembers> directMemberList = tMemberMapper.selectBatchIds(directIdList);
            BigDecimal directInvest = directMemberList.stream().map(TMembers::getInvest).reduce(BigDecimal.ZERO,BigDecimal::add);
            vo.setDirectInvest(directInvest);
        }
        vo.setInviteCount((long)directList.size());
        vo.setMemberList(memberVoList);
        return vo;
    }

    @Override
    public boolean logout() {
        UserIdContext.clear();
        return true;
    }

    @Override
    public TCustomers buildRandomCustomer(){
        return TCustomers.builder()
                .nickname(randomName())
                .password(defaultPassword())
                .txPassword(CommonConstants.DEFAULT_TX_PWD)
                .inviteCode(randomInviteCode())
                .mobile(randomMobile())
                .countryCode("86")
                .email(randomEmail())
                .source("user")
                .fundAutoInvest(SwitchEnum.OFF.getCode())
                .state(StateEnum.NORMAL.getCode())
                .build();
    }

    @Override
    public List<TCustomers> findAdmins() {
        LambdaQueryWrapper<TCustomers> admQuery = new LambdaQueryWrapper<>();
        admQuery.in(TCustomers::getNickname,
                CommonConstants.DEFAULT_ADMIN,
                CommonConstants.DEFAULT_FINANCIAL,
                CommonConstants.DEFAULT_OPERATION);
        List<TCustomers> admList = tCustomerMapper.selectList(admQuery);
        return admList;
    }

    private String randomInviteCode(){
        return RandomValueUtils.capitalNumber(6);
    }

    private String randomName(){
        return "AnonymousUser-"+ RandomValueUtils.getStr(10, 11);
    }

    private String randomEmail(){
        return RandomValueUtils.getEmail(5, 12);
    }

    private String randomMobile(){
        return RandomValueUtils.getTel();
    }

    private int getUpgradeLevelCount(int upgradeLevel, TMembers member){
        int levelCount = 0;
        switch (upgradeLevel){
            case 1:
                levelCount = member.getV0Count();
                break;
            case 2:
                levelCount = member.getV1Count();
                break;
            case 3:
                levelCount = member.getV2Count();
                break;
            case 4:
                levelCount = member.getV3Count();
                break;
            case 5:
                levelCount = member.getV4Count();
                break;
            case 6:
                levelCount = member.getV5Count();
                break;
            default:
                break;
        }
        return levelCount;
    }

    private boolean verificationCodeCheck(VerificationCodeCheckVO vo){
        String loginType = vo.getLoginType();
        LoginTypeEnum loginTypeEnum = LoginTypeEnum.byCode(loginType);
        if(loginTypeEnum==null){
            throw new BaseException(ErrorCode.ILLEGAL_LOGIN_TYPE);
        }
        boolean checkResult = false;
        String inputCode = vo.getInputCode();
        if(StringUtils.isBlank(inputCode)){
            throw new BaseException(ErrorCode.REQUIRED_VERIFICATION_CODE);
        }
        if(LoginTypeEnum.isMobile(loginType)){
            String countryCode = vo.getCountryCode();
            String mobile = vo.getMobile();
            if(StringUtils.isBlank(countryCode) || StringUtils.isBlank(mobile)){
                throw new BaseException(ErrorCode.REQUIRED_MOBILE);
            }
            if(!"86".equalsIgnoreCase(countryCode)) {
                mobile = countryCode + mobile;
            }
            String key = CacheConstants.CACHE_VERIFICATION_CODE+mobile;
            String countKey = CacheConstants.CACHE_VERIFICATION_COUNT+mobile;
            cacheHolder.decr(countKey, 1);
            Object value = cacheHolder.get(key);
            //log.info("从[{}]获取验证码:[{}], 输入验证码:[{}]", key, value, inputCode);
            if(value==null){
                throw new BaseException(ErrorCode.EXPIRE_VERIFICATION_CODE);
            }
            checkResult = inputCode.equals(value.toString());
        }
        if(LoginTypeEnum.isEmail(loginType)){
            String email = vo.getEmail();
            if(StringUtils.isBlank(email)){
                throw new BaseException(ErrorCode.REQUIRED_EMAIL);
            }
            String key = CacheConstants.CACHE_VERIFICATION_CODE+email;
            String countKey = CacheConstants.CACHE_VERIFICATION_COUNT+email;
            cacheHolder.decr(countKey, 1);
            Object value = cacheHolder.get(key);
            //log.info("从[{}]获取验证码:[{}], 输入验证码:[{}]", key, value, inputCode);
            if(value==null){
                throw new BaseException(ErrorCode.EXPIRE_VERIFICATION_CODE);
            }
            checkResult = inputCode.equals(value.toString());
        }
        return checkResult;
    }
}
